﻿#pragma once

#include <windows.h>

// 注意 windows 一定要放在 gdiplus 前面
#include <gdiplus.h>

// 取消一些 windows 定义的宏
// 注意 far 居然在 gdiplus 里用到
#undef near
#undef far
#undef min
#undef max

#include <xy/ais/context.h>
#include <xy/render/context.h>

namespace xy
{

namespace app
{

class  application
{
  private:
    HWND m_hWnd;                                      // 窗口句柄
    ULONG_PTR m_gdiplusToken;                         // gdi token
    std::shared_ptr<ais::context> m_aisContext;       // 交互上下文
    std::shared_ptr<render::context> m_renderContext; // 绘图上下文
    std::shared_ptr<Gdiplus::Bitmap> m_bitmap;        // 绘图位图

  public:
    /**
     * @brief 注册交互上下文
     *
     * @tparam T
     */
    template <typename T>
        requires(std::is_base_of_v<ais::context, T>)
    std::shared_ptr<T> register_ais_context()
    {
        if (m_renderContext == nullptr)
            return nullptr;

        auto aisContext = std::make_shared<T>(m_renderContext->camera());
        m_aisContext = aisContext;
        return aisContext;
    }

    /**
     * @brief 注册渲染上下文
     *
     * @tparam T
     */
    template <typename T>
        requires(std::is_base_of_v<render::context, T>)
    std::shared_ptr<T> register_render_context(int msaaLevel)
    {
        RECT rect;
        GetWindowRect(m_hWnd, &rect);

        int width = rect.right - rect.left - 16;
        int height = rect.bottom - rect.top - 39;

        auto renderContext = std::make_shared<T>(width * 0.9f, height * 0.9f, msaaLevel);
        m_renderContext = renderContext;
        m_bitmap = std::make_shared<Gdiplus::Bitmap>(width * 0.9f, height * 0.9f, PixelFormat32bppARGB);

        return renderContext;
    }

    /**
     * @brief 创建 Windows 窗口
     *
     * @param[in] hInstance
     * @param[in] nCmdShow
     * @param[in] x
     * @param[in] y
     * @param[in] width
     * @param[in] height
     * @param[in] title
     */
    application(HINSTANCE hInstance, int nCmdShow, int x, int y, int width, int height, const wchar_t *title);

    /**
     * @brief Destroy the application object
     *
     */
    ~application();

    /**
     * @brief 消息循环
     *
     * @return int
     */
    int message_loop();

    /**
     * @brief 绘图时间
     *
     */
    void paint_event();

    /**
     * @brief 窗口大小变化事件
     *
     */
    void resize_event();

    /**
     * @brief 鼠标事件
     *
     * @param[in] message
     * @param[in] wParam
     * @param[in] lParam
     */
    void mouse_event(UINT message, WPARAM wParam, LPARAM lParam);

    /**
     * @brief 键盘事件
     *
     * @param[in] message
     * @param[in] wParam
     * @param[in] lParam
     */
    void key_event(UINT message, WPARAM wParam, LPARAM lParam);

    /**
     * @brief 窗口过程函数
     *
     * @return LRESULT
     */
    static LRESULT CALLBACK wndproc(HWND, UINT, WPARAM, LPARAM);
};

} // namespace app

} // namespace xy
